//========================================================================================
// MerHash.hh
// ---------------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------------
// $Id: //CBT-depot/CBT++/include/MerHash.hh#1 $
//----------------------------------------------------------------------------------------
//
//========================================================================================
//.......1.........2.........3.........4.........5.........6.........7.........8.........9
//

#ifndef _MERHASH
#define _MERHASH

#include "CompBioTools.hh"
#include "Mer.hh"

// want 64-bit Words 
typedef unsigned long long Word;
// want 32-bit Indexes 
typedef unsigned long Index;
// want 8 bit Counts 
typedef unsigned char Count;

#define COUNTBINS (1 << (sizeof(Count) * 8))
typedef enum { DESCRIP, FASTA } State;
// typedef enum { A=0, C=1, G=2, T=3 } Base; 
#define A (0ULL)
#define C (1ULL)
#define G (2ULL)
#define T (3ULL)
#define Akjd (0ULL)
#define Ckjd (1ULL)
#define Gkjd (3ULL)
#define Tkjd (2ULL)

// KJD Keep Paul's encoding for now, just to have as little
// impact on his code as possible. Must change all rev-comp 
// as part of this change. 

//      Hex         Dec            Binary   Encoding Bits
// a = 61 (hex)    97 (dec)      0110 0001   0110 0(00)1   a -> 0
// c = 63 (hex)    99 (dec)      0110 0011   0110 0(01)1   c -> 1
// g = 67 (hex)    103 (dec)     0110 0111   0110 0(11)1   g -> 3
// t = 74 (hex)    116 (dec)     0111 0100   0111 0(10)0   t -> 2

// A = 41 (hex)    65 (dec)      0100 0001   0100 0(00)1   A -> 0
// C = 43 (hex)    67 (dec)      0100 0011   0100 0(01)1   C -> 1
// G = 47 (hex)    71 (dec)      0100 0111   0100 0(11)1   G -> 3 
// T = 54 (hex)    84 (dec)      0101 0100   0101 0(10)0   T -> 2 
//#define A (0ULL)
//#define C (1ULL)
//#define G (3ULL)
//#define T (2ULL)


//#ifndef BASESUNUSED
//#define BASESUNUSED (0) 
//#endif
//extern int BASESUNUSED;

#define BITSPERBASE (2UL)
//#define BITSUNUSED (BASESUNUSED*BITSPERBASE)
extern int BITSUNUSED;
#define BITSPERWORD (sizeof(Word) * 8UL)
#define BASEMASK ((1UL << BITSPERBASE) - 1)
#define BASESPERBYTE (8/BITSPERBASE)
#define BASESPERWORD (BITSPERWORD/BITSPERBASE)

// Some prime number close to the hash size.  
// #define HASHSIZE (101000777)    // Roughly 900MB of space. 
// #define HASHSIZE (357913931)   // Roughly 3 GB of space. 
// #define HASHSIZE  79999987        // Roughly 800 MB of space. 
// #define HASHSIZE  59999999        // 687 MB
// #define HASHSIZE  49999991          // 429 MB
// #define HASHSIZE  29999947   // 258MB  
#ifndef HASHSIZE
#define HASHSIZE  39999943   // 340 MB
#endif

// KJD Currently there are 70,350,790 mers in the kill list (count > 8). 
// So a hash that is 80 million in size should hold them at a load factor
// of 87.9%.   A bit tight for good hash performance.   

// KJD This is a crazy mixture of global variables and class-like structure. 
// I wrote as a class, but then had some performance problems, so I tried
// making these variables global to save on the dereferencing operations. 
// However, I think the slow performance was due to hash walking with too 
// small steps, so when I get a chance I'll move these back into the class.  

//State mState;

//  extern Word *mHash; // [HASHSIZE]; 
//  extern Count *mCount; // [HASHSIZE] = {0}; 
//  extern Index mNmers; // global nmer count 
//  extern Index mDistinct;
//  extern Index mReads; /* global read count */

//extern int mProc;

//extern unsigned long  mCollisions;
//extern unsigned long long  mWalkSteps;
//extern unsigned long  mTotalInsertions;
//extern long mHashSize;


//#define shiftin(w, r, b, g, p) { w <<= (BITSPERBASE + BITSUNUSED);  w >>= BITSUNUSED; w |= b;\
//      r >>= BITSPERBASE; r |= (~b & BASEMASK) << (BITSPERWORD - (BITSPERBASE + BITSUNUSED)); \
//     g++; \
//     if (g >= (BASESPERWORD-BASESUNUSED)) { \
//       Word tw; \
//       tw = (w <= r)? w : r; \
//       addcount(tw, p); \
//     } \
//}

#define shiftinmer(w,b)  {			\
    w <<= (BITSPERBASE + BITSUNUSED);		\
    w >>= BITSUNUSED;				\
    w |= b;					\
  }


#define findslot(probe,w) {				\
    Index up, lo,start,step;				\
    lo = w & 0xFFFFFFFF;				\
    up = w >> 32;					\
    probe = start = (~lo ^ up) % mHashSize;		\
    if (mCount[probe] && mHash[probe] != w){		\
      mCollisions++;					\
    };							\
    step = ((lo + up) & 0x1FE) + 17;			\
    while (mCount[probe] && mHash[probe] != w) {	\
      mWalkSteps++;					\
      probe += step;					\
      if (probe >= mHashSize) probe -= mHashSize;	\
      if (probe == start) break;			\
    };							\
  }


//------------------------------------------------------------------------------------------
// * MerHash
//------------------------------------------------------------------------------------------
//
class MerHash{
 public:


  State mState;
  Word *mHash; // [HASHSIZE]; 
  Count *mCount; // [HASHSIZE] = {0}; 
  Index mNmers; // global nmer count 
  Index mDistinct;
  Index mReads; /* global read count */

  long mHashSize;  
  int mProc;
  char *tempbuf;
  
  unsigned long  mCollisions;
  unsigned long long  mWalkSteps;
  unsigned long  mTotalInsertions;
  
  // Former constants.  Now run-time variables. 
  int BASESUNUSED; //=0; // Default is 32-mer. 
  int BITSUNUSED;//; =(BASESUNUSED*BITSPERBASE);
  
  int mMerSize;

  MerHash(){
    BASESUNUSED = 0; // Default is 32-mer. 
    BITSUNUSED=(BASESUNUSED*BITSPERBASE);

    tempbuf = new char[5000];

    mNmers = 0; // global nmer count 
    mDistinct = 0;
    mReads = 0;
    
    mWalkSteps = 0;
    mCollisions = 0;
    mTotalInsertions = 0;    
    SetMerSize(32);
  }

  void SetMerSize(int merSize);

  ~MerHash(){
    delete tempbuf;
    delete mHash;
    delete mCount;
  }

  void Create(long HashSize){
    mHashSize = HashSize;

    mHash = (Word *) malloc(sizeof(Word) * mHashSize);
    mCount = (Count *) calloc(sizeof(Count), mHashSize);
  }

  int size(){
    return(mTotalInsertions);
  }

  void Add(istream &fin); 

  void Add(string &mer,int count);
  void Add(char* mer,int count);
  void Add(Word w, int count);
  void AddCount(Word w, int proc);  

  void ShiftIn(Word &w, Word &r,Word b,int &g,int &p);
  void ShiftInMer(Word &w, Word base);

  Word String2Mer(string &MerStr);
  Word CString2Mer(const char *);
  char *Mer2CString(Word w);
  Word RevComp(Word w);


  bool ContainsFwd(Word w);
  bool ContainsFwd(Word w,Count &count);
  bool ContainsFwd(const char *MerStr);

  // Handles difference between ASCII encoding and 0123 encoding. 
  bool Contains(Mer M);
  bool Contains(Mer M,int &count);
  Word Mer2Word(Mer M);

};

#endif 



